import { message as antdMessage } from 'antd';
import { JSONPath } from 'jsonpath-plus';
import lodash from 'lodash';
import { fetcher } from './fetcher';
import { storage } from './storage';

const cqrsEndpoint = '/api/dde/cqrs';
const command = (url: string, options: any): Promise<any> => {
  const { successTip, ...rest } = options;
  return fetcher(url, { ...rest, throwError: false }).then((it) => {
    const message = window.sfw?.app?.message || antdMessage;
    if (it?.error) {
      if (it?.error.status >= 400 && it?.error.status < 500) {
        message.warning(it?.error.message);
      } else {
        message.error(it?.error.message);
      }
      return it;
    }
    message.success(successTip?.content ?? '操作成功');
    return it;
  });
};

const commandV2 = (options: any): Promise<any> => {
  return command(cqrsEndpoint, options);
};

const cacheCodes = [
  'DynamicComponentByCodeOneQuery',
  'DictionaryStatusLabelSelectListQuery',
];

const getCacheKey = (body: Record<string, any>) => {
  const code = body.code;
  // {
  //   "code": "DynamicComponentByCodeOneQuery",
  //   "params": {
  //     "componentCode": "receiptAndPaymentUploadEvidenceFileForm"
  //   }
  // }
  if (lodash.isEqual(`DynamicComponentByCodeOneQuery`, code)) {
    return `sfw4_cache_${code}_${body.params.componentCode}`;
  }
  return `sfw4_cache_${code}_${JSON.stringify(body.params)}`;
};

const queryOfCache = (url: string, options: any): Promise<any> => {
  const {
    body,
    ttl = 3600000, // 缓存有效时间（这里是1小时，单位毫秒）
    noCache = false,
  } = options;

  const code = body.code;
  if (!lodash.isEmpty(code) && !noCache) {
    if (lodash.includes(cacheCodes, code)) {
      const cacheKey = getCacheKey(body);
      const cachedData = storage.getItem(cacheKey);
      const cacheTime = storage.getItem<string>(`${cacheKey}_time`);
      if (cachedData && cacheTime && Date.now() - parseInt(cacheTime) < ttl) {
        // 如果缓存存在且未过期，直接返回缓存数据
        return Promise.resolve(cachedData);
      }

      return fetcher(url, { ...options, throwError: true }).then((result) => {
        // 更新缓存
        storage.setItem(cacheKey, result);
        storage.setItem(`${cacheKey}_time`, Date.now());
        return result;
      });
    }
  }

  return fetcher(url, { ...options, throwError: true });
};

const query = (url: string, options: any): Promise<any> => {
  return queryOfCache(url, { ...options, throwError: true }).then((it) => ({
    data: { data: it.data },
    success: !it.error,
  }));
};

const queryV2 = (options: any): Promise<any> => {
  return query(cqrsEndpoint, options);
};

const queryForForm = (options: any): Promise<any> => {
  return query(cqrsEndpoint, options).then((it) => it.data?.data);
};

const queryPage = (url: string, options: any): Promise<any> => {
  return queryOfCache(url, { ...options, throwError: true }).then((it) => ({
    data: it.data.content,
    total: it.data.total,
    success: !it.error,
  }));
};

const queryPageV2 = (options: any): Promise<any> => {
  return queryPage(cqrsEndpoint, options);
};

const queryPageForSelect = (options: any): Promise<any> => {
  return queryPage(cqrsEndpoint, options).then((it) => it.data);
};

const getOperator = (
  field: string,
  operatorMapping?: Record<string, string>,
): any => {
  if (!operatorMapping) {
    return 'like';
  }
  return operatorMapping[field] ? operatorMapping[field] : 'like'; //TODO 动态处理操作符
};

const tableRequestParamsSortToQuery = (sort: any) => {
  if (lodash.isEmpty(sort)) {
    return null;
  }
  return lodash.entries(sort).map(([field, value]) => {
    let property = field;

    const voFieldMark = 'Vo';
    if (lodash.endsWith(field, voFieldMark)) {
      property = `${lodash.replace(field, voFieldMark, '')}Value`;
    }

    const moFieldMark = 'Mo';
    if (lodash.endsWith(field, moFieldMark)) {
      property = `${lodash.replace(field, moFieldMark, '')}Id`;
    }

    return {
      property,
      direction:
        value === 'ascend' ? 'ASC' : value === 'descend' ? 'DESC' : null,
    };
  });
};

const tableRequestParamsQueryToQuery = (
  queryParams: any,
  operatorMapping?: Record<string, string>,
) => {
  return lodash
    .entries(queryParams)
    .filter(([, value]) => {
      if (lodash.isString(value)) {
        return lodash.trim(value).length > 0;
      }
      if (lodash.isBoolean(value)) {
        return true;
      }
      return !lodash.isEmpty(value);
    })
    .map(([key, valueTemp]) => {
      let field = key;
      let value = valueTemp as any;
      let operator;

      const voFieldMark = 'Vo';
      if (lodash.endsWith(key, voFieldMark)) {
        field = `${lodash.replace(key, voFieldMark, '')}Value`; // statusVo -> statusValue
        value = value?.value;
        operator = 'is';
      }

      const moFieldMark = 'Mo';
      if (lodash.endsWith(key, moFieldMark)) {
        field = `${lodash.replace(key, moFieldMark, '')}Id`; // modelMo -> modelId
        value = value?.value;
        operator = 'is';
      }

      const betweenDateFieldMark = 'BetweenDate';
      if (lodash.endsWith(key, betweenDateFieldMark)) {
        field = `${lodash.replace(key, betweenDateFieldMark, '')}`; // transactionTimeBetweenDate -> transactionTime
        operator = 'betweenDate';
      }

      const betweenDateTimeFieldMark = 'BetweenDateTime';
      if (lodash.endsWith(key, betweenDateTimeFieldMark)) {
        field = `${lodash.replace(key, betweenDateTimeFieldMark, '')}`; // transactionTimeBetweenDateTime -> transactionTime
        operator = 'betweenDateTime';
      }

      const inFieldMark = 'In';
      if (lodash.endsWith(key, inFieldMark)) {
        field = `${lodash.replace(key, inFieldMark, '')}`; // businessTypeValueIn -> businessTypeValue
        operator = 'in';
      }

      const isFieldMark = 'Is';
      if (lodash.endsWith(key, isFieldMark)) {
        field = `${lodash.replace(key, isFieldMark, '')}`; // contractIdIs -> contractId
        operator = 'is';
      }

      return {
        field,
        operator: lodash.isEmpty(operator)
          ? getOperator(key, operatorMapping)
          : operator,
        value,
      };
    });
};

const tableRequestParamsKeywordToQuery = (
  keyword: string,
  keywordFields?: (k: string) => string[],
) => {
  if (lodash.trim(keyword).length === 0) {
    return null;
  }
  if (!keywordFields) {
    throw Error('请配置 keywordFields ');
  }
  return {
    operator: 'or',
    subCriteria: keywordFields(keyword)?.map((field) => ({
      field,
      operator: 'like',
      value: keyword,
    })),
  };
};

const tableRequestParamsToQuery = (tableParams: {
  params: any;
  sort: any;
  filter: any;
  keywordFields?: (k: string) => string[];
  operatorMapping?: Record<string, string>;
}): any => {
  const { params, sort, filter, keywordFields, operatorMapping } = tableParams;
  const {
    current: page,
    pageSize: size,
    keyword,
    ...queryParams
  } = params || {};
  const { filterOfParams, ...queryParamsOfRest } = queryParams;
  console.info('filter 待集成功能', filter);

  const queryCriteria = tableRequestParamsQueryToQuery(
    { ...queryParamsOfRest, ...filterOfParams },
    operatorMapping,
  );

  const orders = tableRequestParamsSortToQuery(sort);
  const keywordCriteria = tableRequestParamsKeywordToQuery(
    keyword,
    keywordFields,
  );
  const allCriteria = keywordCriteria
    ? [...queryCriteria, keywordCriteria]
    : queryCriteria;
  const criteria = lodash.isEmpty(allCriteria)
    ? null
    : {
        operator: 'and',
        subCriteria: allCriteria,
      };

  return {
    page,
    size,
    keyword,
    criteria,
    orders,
  };
};

const formValuesToPayloadCommand = (
  values: Record<string, any>,
): Record<string, object> => {
  const { _payload, ...restValues } = values;

  if (!_payload) {
    return values;
  }

  let payload;
  if (lodash.isFunction(_payload)) {
    payload = _payload(restValues);
  } else {
    payload = _payload;
  }

  return {
    data: restValues,
    ...payload,
  };
};

const formValuesToCommand = (
  values: Record<string, any>,
  optionsParam: Record<string, any>,
): Record<string, any> | [Record<string, any>] => {
  if (!lodash.isObject(values)) {
    return values;
  }

  let options = optionsParam;
  if (!optionsParam) {
    options = {};
  }
  const isRoot = !options?.pathName;
  options.pathName = options?.pathName || '$';

  const newValues = lodash.cloneDeep(values) as any;
  // const v=window.sfw.lodash.omit(values, [\"supplier\",\"contract\"]); v.supplierId=values.supplier.value; v.supplierCode=values.supplier.code;if(window.sfw.lodash.has(values, \"contract\")){ v.contractId=values.contract.value};

  lodash.entries(newValues).forEach(([field, value]) => {
    const pathName = `${options.pathName}.${field}`;
    let newValue: any = value;

    // Mo，模型对象转换，可以在下拉框中正确显示
    const moFieldMark = 'Mo';
    if (lodash.endsWith(field, moFieldMark)) {
      const moField = lodash.replace(field, moFieldMark, ''); // modelMo -> model

      // lodash.assign(newValues, { [`${moField}Id`]: newValue?.value });
      const valueSuffix = options.moMapping?.[field] || 'Id';
      lodash.assign(newValues, {
        [`${moField}${valueSuffix}`]: newValue?.value,
      });
      if (lodash.indexOf(lodash.keys(newValue), 'code') !== -1) {
        lodash.assign(newValues, { [`${moField}Code`]: newValue?.code });
      }

      // vehicleMo: { pick: [`vehicleAxleNum`, `vehicleCheckLoadCapacity`, { vehicleLicensePlate: `label` }] }
      const vehicleMoOption = lodash.first(
        JSONPath({ json: options, path: `${pathName}.pick` }),
      ) as Record<string, any>;
      if (vehicleMoOption) {
        lodash.forEach(vehicleMoOption, (it) => {
          let keyName = it;
          let keyValue = it;
          if (lodash.isObject(it)) {
            const nameAndValue = lodash.head(lodash.entries(it));
            keyName = nameAndValue?.[0];
            keyValue = nameAndValue?.[1];
          }
          lodash.assign(newValues, { [keyName]: newValue?.[keyValue] });
        });
      }
      lodash.unset(newValues, field);
      return;
    }

    // Mao，模型自动补充对象转换
    const maoFieldMark = 'Mao';
    if (lodash.endsWith(field, maoFieldMark)) {
      const maoField = lodash.replace(field, maoFieldMark, ''); // modelMao -> model

      const valueSuffix = options.maoMapping?.[field] || 'Name';
      lodash.assign(newValues, {
        [`${maoField}${valueSuffix}`]: newValue?.value,
      });
      if (lodash.indexOf(lodash.keys(newValue), 'id') !== -1) {
        lodash.assign(newValues, { [`${maoField}Id`]: newValue?.id });
      }
      if (lodash.indexOf(lodash.keys(newValue), 'code') !== -1) {
        lodash.assign(newValues, { [`${maoField}Code`]: newValue?.code });
      }
      lodash.unset(newValues, field);
      return;
    }

    // Vo，字典对象转换，可以在下拉框中正确显示
    const voFieldMark = 'Vo';
    if (lodash.endsWith(field, voFieldMark)) {
      const moField = lodash.replace(field, voFieldMark, ''); // statusVo -> status

      lodash.assign(newValues, { [`${moField}Value`]: newValue?.value });
      lodash.unset(newValues, field);
      return;
    }

    // Files，数组文件，需要转换成对象
    const filesFieldMark = 'Files';
    if (lodash.endsWith(field, filesFieldMark)) {
      lodash.assign(newValues, {
        [field]: {
          files: newValue,
        },
      });
      return;
    }

    // 范围值转换
    const startFieldMark = 'Start';
    const endFieldMark = 'End';
    const startEndFieldMark = 'StartEnd';
    if (lodash.endsWith(field, startEndFieldMark)) {
      const rangeField = lodash.replace(field, startEndFieldMark, ''); // date
      const startFieldName = `${rangeField}${startFieldMark}`; // dateStart
      const endFieldName = `${rangeField}${endFieldMark}`; // dateEnd
      lodash.assign(newValues, {
        [startFieldName]: newValue?.[0],
        [endFieldName]: newValue?.[1],
      });
      lodash.unset(newValues, field);
      return;
    }

    // 值是个 List 情况
    if (!lodash.isEmpty(newValue?.['content'])) {
      options.hasContentFields = lodash.concat(
        options.hasContentFields || [],
        field,
      );
    }

    if (lodash.isArray(value)) {
      const _defaultValue = lodash.first(
        JSONPath({ json: options, path: `${pathName}.defaultValue` }),
      ) as Record<string, any>;
      const defaultValueByFunc = lodash.isFunction(_defaultValue)
        ? _defaultValue(newValues)
        : _defaultValue;

      // 删除处理开始
      const oldContents = lodash.first(
        JSONPath({
          json: options.oldValues,
          path: pathName,
        }),
      ) as Record<string, any>[] | undefined;
      const oldContentIds = oldContents?.map((it: any) => it.id);
      const deleteIds = lodash.filter(
        oldContentIds,
        (id) => !lodash.some(newValue, (it) => lodash.isEqual(it.id, id)),
      );
      // 删除 payload
      const deletePayloadList =
        deleteIds?.map((id) => ({
          id,
          _payload: {
            ...defaultValueByFunc._payload,
            payloadName: 'delete',
            deleteCmd: true,
          },
        })) || [];
      // 删除处理结束

      const contentValues = [...newValue, ...deletePayloadList]?.map(
        (it: any) => {
          if (!lodash.isObject(it)) {
            return it;
          }
          return formValuesToCommand({ ...it }, { ...options, pathName });
        },
      );

      lodash.assign(newValues, {
        [field]: contentValues,
      });
      return;
    }

    // 数组也会被识别为对象，所以放上面转换
    if (lodash.isObject(value)) {
      const pathName = `${options.pathName}.${field}`;
      lodash.assign(newValues, {
        [field]: formValuesToCommand(newValue, { ...options, pathName }),
      });
      return;
    }
  });

  const _defaultValue = lodash.first(
    JSONPath({ json: options, path: `${options.pathName}.defaultValue` }),
  ) as any;
  let defaultValue: any;
  if (_defaultValue) {
    const query = (data: any) => ({
      criteria: {
        field: 'id',
        value: data.id,
        operator: 'is',
      },
    });
    const defaultValueByFunc = lodash.isFunction(_defaultValue)
      ? _defaultValue(newValues)
      : _defaultValue;
    const createPayload = { payloadName: `create` };
    const changePayload = { payloadName: `change`, query: query(newValues) };
    const createOrChangePayload = newValues._payload?.payloadName
      ? null
      : newValues.id
      ? changePayload
      : createPayload;
    defaultValue = {
      ...defaultValueByFunc,
      _payload: {
        ...defaultValueByFunc._payload,
        ...newValues._payload,
        ...createOrChangePayload,
      },
    };
  }
  console.debug(
    '[DC] formValuesToCommand, ',
    options.pathName,
    newValues,
    defaultValue,
  );
  const newValuesMerge = { ...newValues, ...defaultValue };

  if (isRoot && options?.multiple) {
    // 如果是根对象并且是多个模型保存则进行处理，合并 _payload 到数据中
    const v = formValuesToPayloadCommand(newValuesMerge);
    // 创建或修改 payload
    const contents = JSONPath({
      json: v,
      path: `${options.pathName}..content[*]`,
    }) as Record<string, any>[];

    let payloadList: Record<string, any>[];
    if (options.isBatch) {
      payloadList = contents.map((it) => {
        const data = {
          ...it.data,
          ...lodash.omit(v, options.hasContentFields),
        };
        return {
          ...it,
          data,
        };
      });
    } else {
      const mainAndSubPayloadList = [
        { ...v, data: lodash.omit(v.data, options.hasContentFields) },
        ...contents,
      ];
      payloadList = mainAndSubPayloadList.map((it) => {
        return {
          ...it,
        };
      });
    }

    const query = (data: any) => ({
      criteria: {
        field: 'id',
        value: data.id,
        operator: 'is',
      },
    });
    console.log('payloadList', payloadList);

    /*
     * {
     *   "code": "SD001",
     *   "customerName": "新一电",
     *   "cargoMo": {
     *     "id": "xid",
     *     "name": "x号煤",
     *     "value": "xid",
     *     "label": "x号煤"
     *   },
     *   "salesOrderItems": [
     *     "content": [
     *       {
     *         "cargoMo": {
     *           "id": "xid",
     *           "name": "x号煤",
     *           "value": "xid",
     *           "label": "x号煤"
     *         },
     *         "unitPrice": 420,
     *         "quantity": 35,
     *         "totalPrice": 14700
     *       }
     *     ]
     *   }
     * }
     *
     * [
     *   {
     *     "resultAlias": "orderCreated",
     *     "modelName": "SalesOrder",
     *     "data": {
     *       "code": "SD001",
     *       "customerName": "新一电",
     *       "cargoId": "xid",
     *       "id": "$$.Spel.#root.get('idWorker').nextId()",
     *       "statusValue": "NOT_STARTED"
     *     }
     *   },
     *   {
     *     "modelName": "SalesOrderItem",
     *     "data": {
     *       "unitPrice": 420,
     *       "quantity": 35,
     *       "totalPrice": 14700,
     *       "cargoId": "xid",
     *       "salesOrderId": "$$.orderCreated.data.id"
     *     }
     *   }
     * ]
     *
     */
    return payloadList.map((it) => {
      if (!(!it.data?.id || lodash.startsWith(it.data?.id, '$'))) {
        it.query = query(it.data);
      }
      return it;
    });
  }
  return formValuesToPayloadCommand(newValuesMerge);
};

const queryToFormValues = (
  queryObject: Record<string, any>,
  options: Record<string, any>,
): Record<string, object> => {
  if (!lodash.isObject(queryObject)) {
    return queryObject;
  }
  const newValues = lodash.cloneDeep(queryObject);
  // const c = this.record; return {...c, supplierVo:{value:c.supplier.id,label:c.supplier.name},contract:{value:c.contract.id,label:c.contract.name}, dateStartEnd:[c.dateStart, c.dateEnd]};

  lodash.entries(newValues).forEach(([field, value]) => {
    let newValue: any = value;

    // Mo，模型对象转换，可以在下拉框中正确显示
    const moFieldMark = 'Mo';
    if (lodash.endsWith(field, moFieldMark)) {
      if (!value) {
        return;
      }
      lodash.assign(newValues, {
        [field]: !newValue
          ? null
          : {
              ...value,
              label: newValue.name,
              value: newValue.id,
            },
      });
      return;
    }

    // Mao，模型自动补充对象转换
    const maoFieldMark = 'Mao';
    if (lodash.endsWith(field, maoFieldMark)) {
      const maoFieldName = lodash.replace(field, maoFieldMark, ''); // partya
      if (!value) {
        return;
      }
      const maoObjects = lodash
        .entries(newValues)
        .filter(([field]) => {
          return lodash.startsWith(field, maoFieldName);
        })
        .map(([field, value]) => {
          const subField = lodash.camelCase(
            lodash.replace(field, maoFieldName, ''),
          );
          if (lodash.isEqual(subField, 'mao')) {
            return lodash.isString(value) ? { id: value } : { ...value };
          }
          return {
            [subField]: value,
          };
        });
      const maoObject = lodash.reduce(
        maoObjects,
        (result, value) => {
          return lodash.assign(result, value);
        },
        {} as Record<string, any>,
      );

      const valueSuffix =
        lodash.camelCase(options?.maoMapping?.[field]) || 'name';
      lodash.assign(newValues, {
        [field]: {
          ...maoObject,
          label: maoObject[valueSuffix],
          value: maoObject[valueSuffix],
        },
      });
      return;
    }

    // Files，数组文件，需要转换成对象
    const filesFieldMark = 'Files';
    if (lodash.endsWith(field, filesFieldMark)) {
      if (!value) {
        return;
      }
      lodash.assign(newValues, { [field]: newValue?.files });
      return;
    }

    // 范围值转换
    const startFieldMark = 'Start';
    const endFieldMark = 'End';
    if (lodash.endsWith(field, startFieldMark)) {
      const rangeField = lodash.replace(field, startFieldMark, ''); // date
      const endFieldName = `${rangeField}${endFieldMark}`; // dateEnd
      const rangeFieldName = `${rangeField}${startFieldMark}${endFieldMark}`; // dateStartEnd
      if (
        lodash.endsWith(field, startFieldMark) &&
        // @ts-ignore
        !lodash.isEmpty(newValues[endFieldName])
      ) {
        lodash.assign(newValues, {
          [rangeFieldName]: [value, lodash.get(newValues, endFieldName)],
        });
        return;
      }
    }

    if (lodash.isObject(value)) {
      lodash.assign(newValues, {
        [field]: queryToFormValues(newValue, options),
      });
      return;
    }

    if (lodash.isArray(value)) {
      lodash.assign(newValues, {
        [field]: newValue.map((it: any) => {
          if (!lodash.isObject(it)) {
            return it;
          }
          return queryToFormValues({ ...it }, options);
        }),
      });
      return;
    }
  });

  console.debug('[DC] queryToFormValues, ', newValues);
  return newValues as any;
};

export const cqrs = {
  command,
  commandV2,
  query,
  queryV2,
  queryForForm,
  queryPage,
  queryPageV2,
  queryPageForSelect,
  tableRequestParamsToQuery,
  formValuesToCommand,
  queryToFormValues,
};
